(function($) {
	$.m.zTree = {
	    index : 1,
	    treeTemplate :
	        '<ul id="treeSelect{id}" class="ztree"></ul>',

        selectTreeTemplate :
        	'<div id="treeContent{id}" class="m-popover popover fade bs-popover-bottom show" style="display:none;" role="tooltip"><div class="arrow"></div><h3 class="popover-header">'+$.i18n.zTree.choose_node+'</h3><div class="popover-body">{tree}</div></div>',

        autocompleteTemplate :
	        '<div class="m-input-icon m-input-icon--right m-typeahead">' +
	            '<input type="text" class="form-control m-input" placeholder="'+$.i18n.zTree.enter_to_search+'" id="searchName{id}">' +
	            '<span class="m-input-icon__icon m-input-icon__icon--right"><span><i class="la la-search"></i></span></span>' +
	        '</div>',
	    /**
	     * 初始化可移动树
	     */
	    initMovableTree : function(config) {

	    	config.renameUrl = config.renameUrl || (config.urlPrefix + "/ajax/{id}/rename?newName={newName}");
	        config.removeUrl = config.removeUrl || (config.urlPrefix + "/ajax/{id}/delete");
	        config.addUrl = config.addUrl || (config.urlPrefix + "/ajax/{id}/appendChild?name={name}");
	        config.moveUrl = config.moveUrl || (config.urlPrefix + "/ajax/{sourceId}/{targetId}/{moveType}/move");
	        config.asyncLoadAll = config.asyncLoadAll || false;
	        config.loadUrl = config.loadUrl || (config.urlPrefix + "/ajax/load" +
	                "?async=" + config.async +
	                "&asyncLoadAll=" + config.asyncLoadAll +
	                (config.excludeId ? "&excludeId=" + config.excludeId : "") +
	                (config.onlyDisplayShow ? "&search_EQ_show=true" : ""));

	        if(config.editable != false) {
	            config.editable = true;
	        }

	        if(!config.permission) {
	            config.permission = {};
	        }
	        config.permission = $.extend({
	            create: false,
	            update: false,
	            remove : false,
	            move : false
	        }, config.permission);


	        var setting = {
	            noSwitchIcon:true,
	            async: {
	                enable: config.async,
	                url: config.loadUrl,
	                autoParam:["id"],
	                dataFilter: $.m.zTree.filter
	            },
	            view: {
	                addHoverDom: config.permission.create ? addHoverDom : null,
	                removeHoverDom: config.permission.create ? removeHoverDom : null,
	                selectedMulti: false
	            },
	            edit: {
	                enable: true,
	                editNameSelectAll: true,
	                showRemoveBtn : config.permission.remove,
	                showRenameBtn: config.permission.update,
	                removeTitle: $.i18n.zTree.remove,
	                renameTitle: $.i18n.zTree.rename,
	                drag : {
	                    isMove: config.permission.move,
	                    isCopy : false,
	                    prev: drop,
	                    inner: drop,
	                    next: drop
	                }
	            },
	            data: {
	                simpleData: {
	                    enable: true
	                }
	            },
	            callback:{
	                beforeRemove: function(treeId, treeNode) {
	                	return confirm($.i18n.zTree.confirm_delete);
					},
	                beforeRename : beforeRename,
	                onRemove: onRemove,
	                onRename: onRename,
	                onDrop : onDrop
	            }
	        };

	        if(!config.editable) {
	            setting.edit = {};
	            setting.view.addHoverDom = null;
	            setting.view.removeHoverDom = null;
	        }

	        if(config,setting) {
	            setting = $.extend(true, config.setting, setting);
	            config.setting = setting;
	        }

	        function drop(treeId, nodes, targetNode) {
	        	return targetNode; 

	        }

	        function addHoverDom(treeId, treeNode) {
	            var sObj = $("#" + treeNode.tId + "_span");
	            if (treeNode.editNameFlag || $("#addBtn_" + treeNode.id).length > 0) return;
	            var addStr = "<span class='button add' id='addBtn_" + treeNode.id
	                + "' title='"+$.i18n.zTree.add_child+"' onfocus='this.blur();'></span>";
	            sObj.after(addStr);
	            var btn = $("#addBtn_" + treeNode.id);
	            if (btn)
	                btn.bind("click", function (e) {
	                    onAdd(e, treeId, treeNode);
	                    return false;
	                });
	        }
	        function removeHoverDom(treeId, treeNode) {
	            $("#addBtn_" + treeNode.id).unbind().remove();
	        }

	        function beforeRename(treeId, treeNode, newName) {
	            var oldName = treeNode.name;
	            if (newName.length == 0) {
	                $.m.alert({
	                    message : $.i18n.zTree.require_node_name
	                });
	                return false;
	            }
	            if(!confirm($.i18n.zTree.confirm_rename)) {
	                var zTree = $.fn.zTree.getZTreeObj(treeId);
	                zTree.cancelEditName(treeNode.name);
	                return false;
	            }
	            return true;
	        }
	        /**
	         * 重命名结束
	         * @param e
	         * @param treeId
	         * @param treeNode
	         */
	        function onRename(e, treeId, treeNode) {
	            var url = config.renameUrl.replace("{id}", treeNode.id).replace("{newName}",treeNode.name);
	            $.m.blockPage($.i18n.zTree.operating);
	            $.getJSON(url, function (data) {
	                $.m.unblockPage();
	            });
	        }
	        /**
	         * 删除结束
	         * @param e
	         * @param treeId
	         * @param treeNode
	         */
	        function onRemove(e, treeId, treeNode) {
	            var url = config.removeUrl.replace("{id}", treeNode.id);
	            $.m.blockPage($.i18n.zTree.operating);
	            $.getJSON(url, function (data) {
	                $.m.unblockPage();
	            });
	        }

	        /**
	         * 添加新节点
	         * @param e
	         * @param treeId
	         * @param treeNode
	         */
	        function onAdd(e, treeId, treeNode) {
	            var url = config.addUrl.replace("{id}", treeNode.id).replace("{name}", $.i18n.zTree.new_node_name);
	            $.m.blockPage($.i18n.zTree.operating);
	            $.getJSON(url, function(newNode) {
	                var node = { id:newNode.id, pId:newNode.pId, name:newNode.name, iconSkin:newNode.iconSkin, open: true,
	                    click : newNode.click, root :newNode.root,isParent:newNode.isParent};
	                var newNode = zTree.addNodes(treeNode, node)[0];
//		                zTree.selectNode(newNode);
	                $("#" + newNode.tId + "_a").click();

	                $.m.unblockPage();
	            });
	        }

	        /**
	         * 移动结束
	         * @param event
	         * @param treeId
	         * @param treeNodes
	         * @param targetNode
	         * @param moveType
	         * @param isCopy
	         */
	        function onDrop(event, treeId, treeNodes, targetNode, moveType, isCopy) {
	            if(!targetNode || treeNodes.length == 0) {
	                return;
	            }
	            var sourceId = treeNodes[0].id;
	            var targetId = targetNode.id;
	            var url = config.moveUrl.replace("{sourceId}", sourceId).replace("{targetId}", targetId).replace("{moveType}", moveType);
	            $.m.blockPage($.i18n.zTree.operating);
	            $.getJSON(url, function (newNode) {
	                $.m.unblockPage();
	            });
	        }

	        var autocomplateEnable = config.autocomplete && config.autocomplete.enable;

	        var id = this.index++;
	        var treeStr = (autocomplateEnable ? this.autocompleteTemplate : '') + this.treeTemplate;
	        var container = null;
	        if(config.containerId) {
	            container = $("#" + config.containerId);
	        } else {
	            container = $("body");
	        }
	        container.append(treeStr.replace(/{id}/g, id));
	        var treeSelect = "treeSelect" + id;
	        
	        // expandAll support 
	        var expandAllId = config.expandAllId;
	        if(expandAllId){
	        	var expandAllSetting = $.m.zTree.initExpandAll(treeSelect, expandAllId);
	        	setting = $.extend(true, expandAllSetting, setting);
	            config.setting = setting;
	        }
	        
	        var zTree = $.fn.zTree.init($("#" + treeSelect), setting, config.zNodes);


	        if(autocomplateEnable) {
	            if(!config.autocomplete.minLength) config.autocomplete.minLength = 0;
	            config.autocomplete.enterSearch = true;
	            config.autocomplete.input = $("#searchName" + id);
	            config.autocomplete.async = config.autocomplete.async || config.async;
	            config.autocomplete.select = config.autocomplete.select || function(event, ui) { //按照名字搜索
	                var searchName = ui.item.value;
	                var url = config.loadUrl + "&searchName=" + searchName;
	                zTree.destroy();
	                $.getJSON(url, function(zNodes) {
	                    if(zNodes.length > 0) { //如果没找到节点就不必展示
	                        zTree = $.fn.zTree.init($("#" + treeSelect), setting, zNodes);
	                    }
	                });
	            };
	            config.autocomplete.source = config.autocomplete.source
	                || config.urlPrefix + "/ajax/autocomplete" +
	                  "?a=a"+ //only make it easy to combine params
	                  (config.excludeId ? "&excludeId=" + config.excludeId : "") +
	                  (config.onlyDisplayShow ? "&search_EQ_show=true" : "");

	            config.treeId = treeSelect;
	            $.m.app.initAutocomplete(config.autocomplete);
	        }
	        
	        return treeSelect;

	    },
	    
	    initExpandAll : function(treeId, expandAllId) {
	        var curStatus = "init", curAsyncCount = 0, goAsync = false;
	    	
	    	function beforeAsync() {
				curAsyncCount++;
			}
			function onAsyncSuccess(event, treeId, treeNode, msg) {
				curAsyncCount--;
				if (curStatus == "expand") {
					expandNodes(treeNode.children);
				} else if (curStatus == "async") {
					asyncNodes(treeNode.children);
				}

				if (curAsyncCount <= 0) {
					curStatus = "";
				}
			}

			function onAsyncError(event, treeId, treeNode, XMLHttpRequest, textStatus, errorThrown) {
				curAsyncCount--;
				if (curAsyncCount <= 0) {
					curStatus = "";
				}
			}
			function check() {
				if (curAsyncCount > 0) {
					return false;
				}
				return true;
			}
			
			function expandAll() {
				if (!check()) {
					return;
				}
				var zTree = $.fn.zTree.getZTreeObj(treeId);
				expandNodes(zTree.getNodes());
				if (!goAsync) {
					curStatus = "";
				}
			}
			function expandNodes(nodes) {
				if (!nodes) return;
				curStatus = "expand";
				var zTree = $.fn.zTree.getZTreeObj(treeId);
				for (var i=0, l=nodes.length; i<l; i++) {
					zTree.expandNode(nodes[i], true, false, false);
					if (nodes[i].isParent && nodes[i].zAsync) {
						expandNodes(nodes[i].children);
					} else {
						goAsync = true;
					}
				}
			}

			function asyncNodes(nodes) {
				if (!nodes) return;
				curStatus = "async";
				var zTree = $.fn.zTree.getZTreeObj(treeId);
				for (var i=0, l=nodes.length; i<l; i++) {
					if (nodes[i].isParent && nodes[i].zAsync) {
						asyncNodes(nodes[i].children);
					} else {
						goAsync = true;
						zTree.reAsyncChildNodes(nodes[i], "refresh", true);
					}
				}
			}
			
			expandAllSetting = {
				callback : {
	        		beforeAsync: beforeAsync,
					onAsyncSuccess: onAsyncSuccess,
					onAsyncError: onAsyncError
	            }
	    	};
			$("#"+expandAllId).bind("click", expandAll);
			
			return expandAllSetting;
	    },

	    /**
	     * @param nodeType 节点类型
	     * @param zNodes 所有节点
	     * @param idDomId 要保存的编号的dom id
	     * @param nameDomId 要保存的名称的dom id
	     * nodeType, zNodes, async, loadUrl, btn, idDomId, nameDomId, autocomplete, autocompleteUrl
	     */
	    initSelectTree : function(config) {
	        config.asyncLoadAll = config.asyncLoadAll || false;
	        config.loadUrl =
	            config.loadUrl || (config.urlPrefix + "/ajax/load" +
	                "?async=" + config.async +
	                "&asyncLoadAll=" + config.asyncLoadAll +
	                (config.excludeId ? "&excludeId=" + config.excludeId : "") +
	                (config.onlyDisplayShow ? "&search_EQ_show=true" : "") +
	                "&onlyCheckLeaf=" + ((config.setting && config.setting.check && config.setting.check.onlyCheckLeaf) ? true : false));
	        var autocomplateEnable = config.autocomplete && config.autocomplete.enable;

	        var id = this.index++;
	        var treeStr = (autocomplateEnable ? this.autocompleteTemplate : '') + this.treeTemplate;
	        var treeContentStr = this.selectTreeTemplate.replace("{tree}", treeStr);
	        $("body").append(treeContentStr.replace(/{id}/g, id));

	        var $id = $("#" + config.select.id);
	        var $name = $("#" + config.select.name);
	        var treeContent = "treeContent" + id;
	        var $treeContent = $("#" + treeContent);
	        var treeSelect = "treeSelect" + id;

	        var setting = {
	            noSwitchIcon:true,
	            async: {
	                enable: config.async,
	                url:config.loadUrl,
	                autoParam:["id"],
	                dataFilter: $.m.zTree.filter
	            },
	            view: {
	                dblClickExpand: false
	            },
	            data: {
	                simpleData: {
	                    enable: true
	                }
	            },
	            callback: {
	                onClick: selectNode,
	                onCheck: selectNode,
	                onAsyncSuccess: onAsyncSuccess 
	            }
	        };

	        if(config.setting) {

	            setting = $.extend(true, config.setting, setting);
	        }

	        function fullName(node) {
	            var names = node.name;

	            while((node = node.getParentNode())) {
	                if(node.root && !config.select.includeRoot) {
	                    break;
	                }
	                names = node.name + " > " + names;
	            }
	            return names;
	        }

	        function onAsyncSuccess(event, treeId, treeNode, msg) {
	        	$.m.zTree.initCheckNodes(treeId, config);
	        };
	        
	        function selectNode(e, treeId, treeNode) {
	            if(!setting.check || !setting.check.enable) {
	                var nodes = zTree.getSelectedNodes();
	                var lastNode = nodes[nodes.length - 1];
	                $name.prop("value", fullName(lastNode));
	                $id.prop("value", lastNode.id);
	                hideMenu();
	            } else {
	                var nodes = zTree.getCheckedNodes(true);
	                var names = "";
	                var ids = "";
	                var onlySelectLeaf = config.setting.check && config.setting.check.onlySelectLeaf;
	                for (var i = 0, l = nodes.length; i < l; i++) {
	                    var node = nodes[i];
	                    if(onlySelectLeaf && node.isParent) {
	                        continue;
	                    }
	                    names += fullName(node) + (i != l - 1 ? "," : "");
	                    ids += node.id + (i != l - 1 ? "," : "");
	                }

	                $name.prop("value", names);
	                $name.change();
	                $id.prop("value", ids);
	                $id.change();
	            }
	        }

	        function showMenu() {
	            var nameOffset = $name.offset();
	            $treeContent.css({left: nameOffset.left + "px", top: nameOffset.top + $name.outerHeight() + "px"}).slideDown("fast");

	            $("body").bind("mousedown", onBodyDown);
	        }

	        function hideMenu() {
	            $treeContent.fadeOut("fast");
	            $("body").unbind("mousedown", onBodyDown);
	        }

	        function onBodyDown(event) {
	            var isBtn = false;
	            config.select.btn.each(function() {
	                isBtn = isBtn ||
	                        event.target == this ||
	                        event.target.parentNode == this ||
	                        (event.target.parentNode ? event.target.parentNode.parentNode : null) == this;
	            });
	            if (!(isBtn || $(event.target).closest(".ui-autocomplete").length > 0  || event.target.id == treeContent || $(event.target).closest("#" + treeContent).length > 0)) {
	                hideMenu();
	            }
	        }

	        config.select.btn.click(function () {
	            if($treeContent.is(":visible")) {
	                hideMenu();
	            } else {
	                showMenu();
	            }
	        });


	        var zTree = null;
	        var initTree = function() {
	            $.m.zTree.prepareZNodes(config.zNodes, config);
	            zTree = $.fn.zTree.init($("#" + treeSelect), setting, config.zNodes);

	            if(autocomplateEnable) {
	                if(!config.autocomplete.minLength) config.autocomplete.minLength = 0;
	                config.autocomplete.enterSearch = true;
	                config.autocomplete.input = $("#searchName" + id);
	                config.autocomplete.async = config.autocomplete.async || config.async;
	                config.autocomplete.select = config.autocomplete.select || function(event, ui) { //按照名字搜索
	                    var searchName = ui.item.value;
	                    var url = config.loadUrl + "&searchName=" + searchName;
	                    zTree.destroy();
	                    $.getJSON(url, function(zNodes) {
	                        var zNodesLength = zNodes.length;
	                        if(zNodesLength > 0) { //如果没找到节点就不必展示
	                            $.m.zTree.prepareZNodes(zNodes, config);
	                            zTree = $.fn.zTree.init($("#" + treeSelect), setting, zNodes);
	                            $.m.zTree.initCheckNodes(treeSelect, config);
	                        }
	                    });
	                };
	                config.autocomplete.source = config.autocomplete.source
	                    || config.urlPrefix + "/ajax/autocomplete?1=1" +
	                       (config.excludeId ? "&excludeId=" + config.excludeId : "") +
	                       (config.onlyDisplayShow ? "&search_EQ_show=true" : "");

	                config.treeId = treeSelect;
	                $.m.app.initAutocomplete(config.autocomplete);
	            }
	        };
	        var initialize = false;
	        //需要回显勾选的话，lazy一定记设置为false
	        if(config.lazy) {
	            config.select.btn.click(function() {
	                if(!initialize) {
	                    initTree();
	                    initialize = true;
	                }
	            });
	        } else {
	            initTree();
	        }

	        return treeSelect;

	    },
	    prepareZNodes : function(zNodes, config) {
	        if(!zNodes) {
	            return;
	        }
	        var zNodesLength = zNodes.length;
	        if(!zNodesLength) {
	            return;
	        }
	        var onlySelectLeaf = config.setting && config.setting.check && config.setting.check.onlySelectLeaf;

	        for(var i = 0; i < zNodesLength; i++) {
	            var node = zNodes[i];

	            if(onlySelectLeaf && node.isParent) {
	                node.nocheck = true;
	            } else {
	                node.nocheck = false;
	            }
	        }
	    },
	    initCheckNodes: function(treeId, config){
	    	var treeObj = $.fn.zTree.getZTreeObj(treeId);
	    	var $id = $("#" + config.select.id);
	        var ids = $id.val().split(",");
	        for(var i=0;i<ids.length;i++){
	        	var node = treeObj.getNodeByParam("id", parseInt(ids[i]), null);
	        	if(node!=null&&!node.checked){
	        		treeObj.checkNode(node, true, true, true);
	        	}
	        }
	        
	    },
	    initMaintainBtn : function(maintainUrlPrefix, tableId, async) {
	        var updateUrl = maintainUrlPrefix + "/{id}/update",
	            deleteUrl = maintainUrlPrefix + "/batch/delete",
	            moveTreeUrl = maintainUrlPrefix + "/{id}/move?async=" + async;

	        $("#moveTree").off("click").on("click", function () {
	            var table = $("#" + tableId);
	            var checkbox = $.table.getFirstSelectedCheckbox(table);
	            if(!checkbox.length) {
	                return;
	            }

	            if(checkbox.filter("[root='true']").length) {
	                $.m.alert({
	                    message : $.i18n.zTree.can_not_move_root
	                });
	                return;
	            }
	            window.location.href = moveTreeUrl.replace("{id}", checkbox.val()) + "&BackURL=" + $.table.encodeTableURL(table);
	            return false;
	        });

	        $("#updateTree").off("click").on("click", function() {
	            var table = $("#" + tableId);
	            var checkbox = $.table.getFirstSelectedCheckbox(table);
	            if(!checkbox.length) {
	                return;
	            }
	            window.location.href = updateUrl.replace("{id}", checkbox.val()) + "?BackURL=" + $.table.encodeTableURL(table);
	        });

	        $("#deleteTree").off("click").on("click", function () {
	            var table = $("#" + tableId);
	            var checkbox = $.table.getAllSelectedCheckbox(table);
	            if(!checkbox.length) {
	                return;
	            }

	            if(checkbox.filter("[root='true']").length) {
	                $.m.alert({
	                    message : $.i18n.zTree.can_not_delete_root
	                });
	                return;
	            }
	            $.m.confirm({
	                message : $.i18n.zTree.confirm_delete,
	                ok : function() {
	                    window.location.href = deleteUrl + "?" + checkbox.serialize() + "&BackURL=" + $.table.encodeTableURL(table);
	                }
	            });
	            return false;
	        });


	    },
	    initMoveBtn : function() {
	        $("#moveAsPrev").click(function() {
	            $("#moveType").val("prev");
	        });
	        $("#moveAsNext").click(function() {
	            $("#moveType").val("next");
	        });
	        $("#moveAsInner").click(function() {
	            $("#moveType").val("inner");
	        });

	    },
	    split : function( val ) {
	    	return val.split( /,\s*/ );
	    },
	    extractLast : function( term ) {
	        return this.split( term ).pop();
	    },
	    filter : function(treeId, parentNode, childNodes) {
	        if (!childNodes) return null;
//		        for (var i=0, l=childNodes.length; i<l; i++) {
//		            childNodes[i].name = childNodes[i].name.replace(/\.n/g, '.');
//		        }
	        return childNodes;
	    }

	};
	
})(jQuery);